OOP II


Ulomki

POZOR:

Da ne bo težav pri testiranju, pri vseh podnalogah začnemo z

              class Ulomek(Ulomek):

To pomeni, da se v razred "skopirajo" vse definicije, ki ste jih v razred Ulomek napisali prej. Seveda pa 1. podnalogo še vedno začnemo z

              class Ulomek():

Druga možnost pa je, da podnaloge vedno začnemo z

              class Ulomek():

a potem v razred vedno napišemo vse metode, ki smo jih definirali v vseh prejšnjih podnalogah!

1. podnaloga

Izven razreda sestavite funkcijo gcd(m, n), ki izračuna največji skupni delitelj števil m in n.

Uradna rešitev

def gcd(m, n):
    while n != 0:
        m, n = n, m % n
    return m

2. podnaloga

Definirajte razred Ulomek, s katerim predstavimo ulomek. Števec in imenovalec sta celi števili, pri čemer je morebiten negativen predznak vedno v števcu. Komponenti naj se imenujeta _st in _im. Ulomki naj bodo vedno okrajšani.

Najprej definirajte konstruktor __init__(self, st, im).

Uradna rešitev

class Ulomek:
    def __init__(self, st, im):
        if im < 0:
            st, im = -st, -im
        d = gcd(st, im)
        self._st = st // d
        self._im = im // d

3. podnaloga

Definirajte metodo __str__, ki predstavi ulomek v obliki "st/im".

Uradna rešitev

class Ulomek(Ulomek):
    def __str__(self):
        return "{0}/{1}".format(self._st, self._im)

4. podnaloga

Definirajte metodo __eq__, ki vrne True če sta dva ulomka enaka, in False sicer.

Uradna rešitev

class Ulomek(Ulomek):
    def __eq__(self, other):
        return self._st == other._st and self._im == other._im

5. podnaloga

Definirajte metodo __add__, ki vrne vsoto dveh ulomkov. Ko definirate to metodo, lahko ulomke seštevate kar z operatorjem +. Na primer:

>>> print(Ulomek(1, 6) + Ulomek(1, 4))
5/12

Uradna rešitev

class Ulomek(Ulomek):
    def __add__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * d + b * c, b * d)

6. podnaloga

Definirajte metodo __sub__, ki vrne razliko dveh ulomkov. Ko definirate to metodo, lahko ulomke odštevate kar z operatorjem -.

Uradna rešitev

class Ulomek(Ulomek):
    def __sub__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * d - b * c, b * d)

7. podnaloga

Definirajte metodo __mul__, ki vrne zmnožek dveh ulomkov. Ko definirate to metodo, lahko ulomke množite kar z operatorjem *.

Uradna rešitev

class Ulomek(Ulomek):
    def __mul__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * c, b * d)

8. podnaloga

Definirajte metodo __truediv__, ki vrne kvocient dveh ulomkov. Ko definirate to metodo, lahko ulomke delite kar z operatorjem /.

Uradna rešitev

class Ulomek(Ulomek):
    def __truediv__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * d, b * c)

9. podnaloga

Definirajte funkcijo priblizek(n), ki vrne vsoto $1/0! + 1/1! + … + 1/n!$. Poglejte, ali je izračunana vrednost blizu števila $e$.

Uradna rešitev

def priblizek(n):
    def fakulteta(i):
        return 1 if i == 0 else i * fakulteta(i - 1)

    return sum((Ulomek(1, fakulteta(i)) for i in range(n + 1)), Ulomek(0, 1))

Preiskovanje dreves

Dan je razred Drevo, ki predstavlja dvojiško drevo. Za osnovno vzemite naslednjo kodo:

class Drevo:

    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
            return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
            return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
           return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'.\
               format(
                   zamik,
                   self.vsebina,
                   self.levo.__repr__(zamik + '             '),
                   self.desno.__repr__(zamik + '              ')
               )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                self.vsebina == other.vsebina and
                self.levo == other.levo and
                self.desno == other.desno))

Konstruktor je že implementiran; prav tako metodi __repr__ in __eq__. Drevo na spodnji (ASCII art) sliki:

     5
   /   \
  3     2
 /     / \
1     6   9

sestavimo takole:

>>> d = Drevo(5,
              levo=Drevo(3, levo=Drevo(1)),
              desno=Drevo(2, levo=Drevo(6), desno=Drevo(9)))

1. podnaloga

Razredu dodajte metodo vsota(self), ki vrne vsoto vseh števil v drevesu. Zgled (kjer je d kot zgoraj):

>>> d.vsota()
26

Uradna rešitev

class Drevo:

    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
          return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
          return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
          return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'.\
            format(
              zamik,
              self.vsebina,
              self.levo.__repr__(zamik + '             '),
              self.desno.__repr__(zamik + '              ')
            )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                 self.vsebina == other.vsebina and
                 self.levo == other.levo and
                 self.desno == other.desno))
    
    def vsota(self):
        if self.prazno:
            return 0
        else:
            return self.vsebina + self.levo.vsota() + self.desno.vsota()

2. podnaloga

Dodajte metodo stevilo_listov(self), ki vrne število listov v drevesu. Zgled (kjer je d kot zgoraj):

>>> d.stevilo_listov()
3

Uradna rešitev

class Drevo():
    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
            return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
            return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
            return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'. \
                format(
                zamik,
                self.vsebina,
                self.levo.__repr__(zamik + '             '),
                self.desno.__repr__(zamik + '              ')
            )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                 self.vsebina == other.vsebina and
                 self.levo == other.levo and
                 self.desno == other.desno))

    def vsota(self):
        if self.prazno:
            return 0
        else:
            return self.vsebina + self.levo.vsota() + self.desno.vsota()

    def stevilo_listov(self):
        if self.prazno:
            return 0
        elif self.levo.prazno and self.desno.prazno:
            return 1
        else:
            return self.levo.stevilo_listov() + self.desno.stevilo_listov()

3. podnaloga

Dodajte metodo minimum(self), ki vrne najmanjše število v drevesu. Če je drevo prazno, naj metoda vrne None. Zgled (kjer je d kot zgoraj):

>>> d.minimum()
1
>>> Drevo().minimum()
None

Uradna rešitev

class Drevo(Drevo):
    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
            return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
            return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
            return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'. \
                format(
                zamik,
                self.vsebina,
                self.levo.__repr__(zamik + '             '),
                self.desno.__repr__(zamik + '              ')
            )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                 self.vsebina == other.vsebina and
                 self.levo == other.levo and
                 self.desno == other.desno))

    def vsota(self):
        if self.prazno:
            return 0
        else:
            return self.vsebina + self.levo.vsota() + self.desno.vsota()

    def stevilo_listov(self):
        if self.prazno:
            return 0
        elif self.levo.prazno and self.desno.prazno:
            return 1
        else:
            return self.levo.stevilo_listov() + self.desno.stevilo_listov()

    def minimum(self):
        if self.prazno:
            return None
        else:
            # Vrednost izraza `a or b` je `a`, če je `bool(a) == True`,
            # in `b` sicer. Torej, to je ekvivalentno izrazu `a if a else b`.
            # Opomba: `bool(None) == False`.
            levi_minimum = self.levo.minimum() or float('inf')
            desni_minimum = self.desno.minimum() or float('inf')
            return min(self.vsebina, levi_minimum, desni_minimum)

Mafija

V neki mafijski organizaciji so člani urejeni hierarhično. Vsakdo razen botra (vrhovnega šefa) ima natanko enega nadrejenega. Vsak mafijec ima lahko pod seboj največ dva podrejena (levega in desnega). Primer takšne mafijske organizacije:

mafija = Drevo('Salvatore', 320,
               levo=Drevo('Bernardo', 200,
                          levo=Drevo('Matteo', 50),
                          desno=Drevo('Carlo', 100,
                                      levo=Drevo('Rosalia', 70),
                                      desno=Drevo('Tommaso', 50))),
               desno=Drevo('Francesco', 120,
                           levo=Drevo('Giuseppe', 70),
                           desno=Drevo('Antonio', 60)))

Mafijci morajo zbirati denar s kriminalnimi dejavnostmi. Tisti, ki imajo podrejene, pa ga poleg tega poberejo še od svojih podrejenih. Ves “prisluženi” in prejeti denar morajo oddati svojemu nadrejenemu.

Boter je posumil, da nekateri člani goljufajo. Nekaj denarja, ki ga poberejo od podrejenih, zadržijo zase. Od vsakega člana je pridobil podatek o tem, koliko denarja je oddal naprej. Podatke je shranil v podatkovno strukturo Drevo, ki je že implementirana.

1. podnaloga

Botra zanima, koliko denarja zaradi goljufov “ponikne”. Želi, da sestavite metodo koliko_ponikne(self), ki vrne skupno vsoto denarja, ki ponikne. Primer (če mafija ustreza zgornjemu drevesu):

>>> mafija.koliko_ponikne()
30

Uradna rešitev

class Drevo:
    
    def __init__(self, *args, **kwargs):
        if len(args) > 0:
            self.prazno = False
            self.ime = args[0]
            self.vrednost = args[1]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self):
        if self.prazno:
            return 'Drevo()'
        else:
            opis = repr(self.ime) + ', ' + repr(self.vrednost)
            if not self.levo.prazno: opis += ', levo={0}'.format(self.levo)
            if not self.desno.prazno: opis += ', desno={0}'.format(self.desno)
            return 'Drevo({0})'.format(opis)
    
    def get_vrednost(self):
        return self.vrednost if not self.prazno else 0
    
    def koliko_ponikne(self):
        if self.prazno:
            return 0
        utajil = max(self.levo.get_vrednost() + self.desno.get_vrednost() - self.get_vrednost(), 0)
        return utajil + self.levo.koliko_ponikne() + self.desno.koliko_ponikne()

2. podnaloga

Ko je boter dognal, koliko denarja ponikne, je totalno po████. Pri priči hoče imeti imena vseh goljufov! Napišite metodo goljufi(self), ki vrne množico goljufov. Vsak goljuf naj bo predstavljen z naborom. Prva kompotenta naj bo ime goljufa, druga komponenta pa količina denarja, ki ga je utajil. Primer:

>>> mafija.goljufi()
{(’Carlo’, 20), (’Francesco’, 10)}

Uradna rešitev

class Drevo:
    def __init__(self, *args, **kwargs):
        if len(args) > 0:
            self.prazno = False
            self.ime = args[0]
            self.vrednost = args[1]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self):
        if self.prazno:
            return 'Drevo()'
        else:
            opis = repr(self.ime) + ', ' + repr(self.vrednost)
            if not self.levo.prazno: opis += ', levo={0}'.format(self.levo)
            if not self.desno.prazno: opis += ', desno={0}'.format(self.desno)
            return 'Drevo({0})'.format(opis)

    def get_vrednost(self):
        return self.vrednost if not self.prazno else 0

    def goljufi(self):
        if self.prazno:
            return set()
        utajil = max(self.levo.get_vrednost() + self.desno.get_vrednost() - self.get_vrednost(), 0)
        return ({(self.ime, utajil)} if utajil > 0 else set()) | self.levo.goljufi() | self.desno.goljufi()

3. podnaloga

Botru se dozdeva, da so najbolj pridne majhne ribe. To so tisti mafijci, ki nimajo pod seboj nobenega podrejenega. Tistim, ki imajo podrejene, se reče velike ribe. Napišite metodo zasluzek(self), ki vrne par (nabor) dveh števili, pri čemer je:

Primer:

>>> mafija.zasluzek()
(300, 50)

Uradna rešitev

class Drevo:
    def __init__(self, *args, **kwargs):
        if len(args) > 0:
            self.prazno = False
            self.ime = args[0]
            self.vrednost = args[1]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self):
        if self.prazno:
            return 'Drevo()'
        else:
            opis = repr(self.ime) + ', ' + repr(self.vrednost)
            if not self.levo.prazno: opis += ', levo={0}'.format(self.levo)
            if not self.desno.prazno: opis += ', desno={0}'.format(self.desno)
            return 'Drevo({0})'.format(opis)

    def get_vrednost(self):
        return self.vrednost if not self.prazno else 0
    def zasluzek(self):
        if self.prazno:
            return 0, 0
        l_male, l_velike = self.levo.zasluzek()
        d_male, d_velike = self.desno.zasluzek()
        mala = self.levo.prazno and self.desno.prazno
        prispevek = max(self.get_vrednost() - self.levo.get_vrednost() - self.desno.get_vrednost(), 0)
        m, v = l_male + d_male, l_velike + d_velike
        if mala:
            m += prispevek
        else:
            v += prispevek
        return m, v

Polinomi

Definirajte razred Polinom, s katerim predstavimo polinom v spremenljivki $x$. Polinom predstavimo s seznamom njegovih koeficientov, kjer je $k$-ti element seznama koeficient pri $x^k$. Na primer, polinom $x^3 + 2 x + 7$ predstavimo s Polinom([7, 2, 0, 1]). Razmislite, kaj predstavlja Polinom([]). Zadnji koeficient v seznamu mora biti neničelen.

1. podnaloga

Sestavite konstruktor __init__(koef), ki nastavi tabelo koeficientov.

Uradna rešitev

class Polinom:

    def __init__(self, koef):
        # že na začetku se znebimo vseh ničelnih vodilnih koeficientov
        zadnji = len(koef)
        while zadnji > 0 and koef[zadnji - 1] == 0:
            zadnji -= 1
        self._koef = koef[:zadnji]

2. podnaloga

Sestavite metodo stopnja, ki vrne stopnjo polinoma. Stopnja ničelnega polinoma je v matematiki po dogovoru $-\infty$, pri nas pa bo $-1$.

Uradna rešitev

class Polinom(Polinom):
    def stopnja(self):
        return len(self._koef) - 1

3. podnaloga

Sestavite metodo __eq__ za primerjanje polinomov.

Uradna rešitev

class Polinom(Polinom):
    def __eq__(self, other):
        return self._koef == other._koef

4. podnaloga

Sestavite metodo __add__ za seštevanje polinomov. Pozor: pri seštevanju se lahko zgodi, da se nekateri začetni koeficienti pokrajšajo: $(x^3 + 2 x + 7) + (- x^3 - 2 x + 10) = 17$

Uradna rešitev

class Polinom(Polinom):
    def __add__(self, other):
        # predpostavimo, da ima levi polinom vsaj toliko členov kot desni
        if len(self._koef) < len(other._koef):
            return other + self

        # nato vzamemo levi polinom in kosoma prištevamo desnega
        koef_vsote = self._koef
        for n, a in enumerate(other._koef):
            koef_vsote[n] += a

        return Polinom(koef_vsote)

5. podnaloga

Sestavite metodo __mul__ za množenje polinomov.

Uradna rešitev

class Polinom(Polinom):
    def __mul__(self, other):
        # če je eden od polinomov ničelen, je ničelen tudi produkt
        if not self._koef or not other._koef:
            return Polinom([])

        # oba polinoma z ničlami podaljšamo do iste dolžine
        levi = self._koef + len(other._koef) * [0]
        desni = other._koef + len(self._koef) * [0]

        koef_prod = [sum(levi[i] * desni[n - i] for i in range(n + 1))
                     for n in range(len(levi))]
        return Polinom(koef_prod)

6. podnaloga

Sestavite metodo __str__, ki predstavi polinom v čitljivi obliki.

>>> print(Polinom([1, -2, 3, -1]))
-x^3 + 3 x^2 - 2 x + 1
>>> print(Polinom([-1, 0, 0, 1]))
x^3 - 1

Uradna rešitev

class Polinom(Polinom):
    def __str__(self):
        def monom(a, n):
            # Sestavimo si člen za monom. Na začetku in na koncu damo še oglate
            # oklepaje, ki jih bomo na koncu sicer pobrisali, da bomo vmes lahko
            # na poseben način obravnavali nekatere kombinacije znakov.
            monom = "[{0} x^{1}]".format(a, n)
            # popravimo potence in koeficiente
            monom = monom.replace("^1]", "") # ^1 pobrišemo le na koncu niza
            monom = monom.replace(" x^0", "") # pobrišemo presledek pred x^0
            monom = monom.replace("[1 x", "x") # 1 mora biti na začetku monoma
            monom = monom.replace("-1 x", "-x")
            monom = monom.replace("[", "").replace("]", "")
            return monom

        # "seštejemo" vse momone z neničelnimi koeficienti
        # pred tem moramo koeficiente obrniti v vrstni red kot v izpisu
        niz = " + ".join(reversed([monom(a, n)
                         for n, a in enumerate(self._koef) if a != 0]))
        niz = niz.replace("+ -", "- ") # popravimo negativne koeficiente

        # če smo dobili niz, ga vrnemo, sicer izpišemo ničelni polinom
        return niz or "0"

Redki vektorji

Vektorjem, ki vsebujejo veliko število ničelnih elementov, pravimo redki vektorji. Namesto običajne predstavitve, pri kateri navedemo vse elemente vektorja, lahko uporabimo bolj kompaktno predstavitev, kjer ničelne elemente izpustimo, za neničelne pa si zapomnimo, na katerih mestih so.

Primer: Vektor

a = [1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 4, 0, 0]

lahko namesto v obliki dolgega seznama z veliko ničlami zapišemo v kompaktni obliki kot slovar, kjer so ključi indeksi neničelnih elementov iz dolgega zapisa:

r = {0: 1, 10: 4, 5: 5, 6: 1}

Vrstni red indeksov seveda ni pomemben, saj imamo slovar. Ničelni vektor predstavimo kar s praznim slovarjem {}.

1. podnaloga

Napišite funkcijo stisni(vektor), ki sprejme vektor kot seznam števil ter vrne redko obliko vektorja, tj. slovar, v katerem so ključi indeksi neničelnih vrednosti, vrednosti pa te neničelne vrednosti.

Primer:

>>> stisni([1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 4])
{0: 1, 10: 4, 5: 5, 6: 1}
>>> stisni([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99])
{0: 1, 15: 99}

Uradna rešitev

def stisni(vektor):
    '''vrne redko obliko vektorja, v obliki indeks : vrednost'''
    sv = dict()
    for i, x in enumerate(vektor):
        if x != 0: # zanimajo nas le neničelni elementi
            sv[i] = x
    return sv

2. podnaloga

Definirajte razred Vektor, s katerim bomo predstavili redke vektorje. Sestavite konstruktor, ki kot parameter sprejme vektor v stisnjeni obliki (slovar) ter ga priredi atributu elementi.

Sestavite še metodo __len__, ki vrne število neničelih elementov redkega vektorja. Metoda nima argumentov (razen obveznega argumenta self, ki pove, da gre za metodo danega razreda). Metoda __len__ se bo izvedla, kadar bomo na objektu tipa Vektor uporabili Pythonovo vgrajeno funkcijo len, kot je prikazano v spodnjem primeru.

Primer:

>>> a = Vektor({0: 1, 10: 4, 5: 5, 6: 1})
>>> a.elementi
{0: 1, 10: 4, 5: 5, 6: 1}
>>> len(a)
4

Uradna rešitev

class Vektor:
    def __init__(self, data):
        self.elementi = data

    def __len__(self):
        return len(self.elementi)

3. podnaloga

Definirajte metodi __add__ in __sub__, s katerima boste definirali operatorja + in - nad objekti tipa Vektor. Gre za običajno seštevanje in odštevanje vektorjev, le da morate upoštevati, da seštevamo oz. odštevamo redki obliki, ki sta predstavljeni s slovarjema.

Vsaka od metod sprejema en argument in sicer drugi vektor, ki nastopa v aritmetični operaciji, vrne pa naj nov objekt tipa Vektor, ki je vsota oz. razlika danih vektorjev.

Nasvet: Upoštevajte, da so v rezultatu lahko neničelne vrednosti le na tistih indeksih, pri katerih je neničeln vsaj en od obeh operandov – pomagajte si z unijo obeh množic indeksov.

Primer:

>>> a = Vektor({0: 1})
>>> b = Vektor({0: 1, 3: 1})
>>> c = a + b
>>> c.elementi
{0: 2, 3: 1}

Uradna rešitev

class Vektor(Vektor):
    def __add__(self, other):
        data = dict()
        keys = set(self.elementi).union(other.elementi) # set iz slovarja pobere le ključe!
        for k in keys:
            # poberemo k kar iz obeh, če ga ni, vzamemo 0
            v = self.elementi.get(k, 0) + other.elementi.get(k, 0)
            if v != 0: # če se vrednosti nista izničili (negativna št!)
                data[k] = v
        return Vektor(data)

    def __sub__(self, other):
        data = dict()
        keys = set(self.elementi).union(other.elementi) # unija ključev, ker je set(slovar) istio kot set(slovar.keys())
        for k in keys:
            v = self.elementi.get(k, 0) - other.elementi.get(k, 0)
            if v != 0: # če se vrednosti nista izničili
                data[k] = v
        return Vektor(data)

4. podnaloga

Definirajte še metodo razsiri, ki vrne običajno (dolgo) obliko vektorja. Pri tem se razširjena oblika ne sme končati z 0, saj drugače razširitev ni enolična (glej drugi primer!)

Primer:

>>> a = Vektor(stisni([1, 0, 0, 0, 0, 0, 0, 0, 9]))
>>> a.elementi
{0: 1, 8: 9}
>>> a.razsiri()
[1, 0, 0, 0, 0, 0, 0, 0, 9]
>>> Vektor(stisni([1, 0, 0, 0])).razsiri()
[1]

Uradna rešitev

class Vektor(Vektor):
    def razsiri(self):
        '''razpihne redki vektor v "običajno" obliko'''
        if self.elementi == dict():
            return list() # prazen seznam
        else:
            # maksimalni indeks je maksimalna vrednost ključa v slovarju
            # vrednosti za vsak indeks bodo bodisi 0 bodisi ustrezne vrednosti iz slovarja
            return [self.elementi.get(i, 0) for i in range(max(self.elementi) + 1)]